/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.webos.java.base.fs.vfs;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSystemOptions;
import org.apache.commons.vfs.FileType;
import org.apache.commons.vfs.VFS;
import org.apache.commons.vfs.provider.sftp.SftpFileSystemConfigBuilder;
import org.apache.commons.vfs.provider.sftp.TrustEveryoneUserInfo;
import org.json.JSONObject;
import org.webos.java.base.DBFileProvider;
import org.webos.java.base.Session;
import org.webos.java.base.fs.FileDescriptor;
import org.webos.java.base.fs.FileException;
import org.webos.java.base.fs.FileManager;
import org.webos.java.base.fs.FileProvider;
import org.webos.java.root.Log;
import org.webos.java.root.RandomGUID;

/**
 *
 * @author Kostya
 */
public class VFSFileProvider implements FileProvider{

    private Session session = null;
    private static String[] protocols = {"sftp://", "smb://"};

    public VFSFileProvider(Session aSession) {
        session = aSession;
    }

    private void fixTypeByExtension(FileDescriptor aDescriptor){
        String name = aDescriptor.getName().toLowerCase();
        String[] fileTypes = FileManager.getPlainFileTypes();
        for (String string : fileTypes) {
            String[] exts = FileManager.getFileType(string).getExtensions();
            if(exts!=null){
                for (String ex : exts) {
                    if(name.endsWith(ex.toLowerCase())){
                        aDescriptor.setType(string);
                        return;
                    }
                }
            }
        }
    }

    private FileObject getFileObject(String url) throws FileException{
        try {
            FileSystemOptions opt = new FileSystemOptions();
            SftpFileSystemConfigBuilder.getInstance().setUserInfo(opt, new TrustEveryoneUserInfo());
            return VFS.getManager().resolveFile(url, opt);
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        throw new FileException("Can't connect to shared resource");
    }

    public FileDescriptor[] listFiles(FileDescriptor aDescriptor) throws FileException {
        String url = "";
        if(VFSFileType.defaultInstance.getCode().equals(aDescriptor.getType())){
            //Root. Construct URL
            try {
                JSONObject object = JSONObject.readJSONObject(aDescriptor.getProvider().getInputStream(aDescriptor));
                String userName = object.optString("username", "");
                String password = session.getExplorer().getSecret(aDescriptor.getId()+"-"+userName, "");
                String protocol = object.optString("protocol", "sftp://");
                boolean found = false;
                for (String pr : protocols) {
                    if(pr.equalsIgnoreCase(protocol))
                        found = true;
                }
                if(!found)
                    throw new FileException("Provided protocol isn't supported now");
                StringBuffer URL = new StringBuffer(protocol);
                if(!"".equals(userName)){
                    URL.append(URLEncoder.encode(userName, "utf-8"));
                    if(!"".equals(password))
                        URL.append(":"+URLEncoder.encode(password, "utf-8"));
                    URL.append("@");
                }
                String host = object.optString("host", "");
                if(host.endsWith("/"))
                    URL.append(host.substring(0, host.length()-2));
                else
                    URL.append(host);
                String path = object.optString("path", "");
                if(!path.startsWith("/"))
                    URL.append("/");
                URL.append(path);
                url = URL.toString();
            } catch (Throwable t) {
                if(t instanceof FileException)
                    throw (FileException)t;
                Log.error(this, "Exception thrown: " + t.getMessage());
                t.printStackTrace();
            }
        }else{
            FileDescriptor desc = aDescriptor;
            url = desc.getObject();
        }
        try {
            FileObject fo = getFileObject(url);
            FileObject[] fos = fo.getChildren();
            List<FileDescriptor> result = new ArrayList<FileDescriptor>();
            for (FileObject f : fos) {
                try {
                    FileType ft = f.getType();
                    FileDescriptor newFile = new FileDescriptor();
                    newFile.setObject(f.getName().getURI());
                    newFile.setId(RandomGUID.getGUID());
                    newFile.setName(f.getName().getBaseName());
                    newFile.setParentFile(aDescriptor);
                    newFile.setProvider(this);
                    newFile.setOwner(session.getLogin());
                    if(ft.equals(FileType.FOLDER)){
                        newFile.setChildrenAllowed(true);
                        newFile.setType("folder");
                        newFile.setAllowedFileTypes(new org.webos.java.base.fs.FileType[]{FolderFileType.DEFAULT_INSTANCE, FileManager.PLAIN_FILE_TYPE});
                    }else{
                        newFile.setChildrenAllowed(false);
                        newFile.setType("text/unknown");
                        fixTypeByExtension(newFile);
                    }
                    DBFileProvider.getIcon(newFile);
                    result.add(newFile);
                } catch (Throwable t) {
//                    Log.error(this, "Exception thrown: " + t.getMessage());
//                    t.printStackTrace();
                }
            }
//            Collections.sort(result, new Comparator<VFSFileDescriptor>(){
//                public int compare(VFSFileDescriptor o1, VFSFileDescriptor o2) {
//                    if(o1.isFolder()!=o2.isFolder())
//                        return o1.isFolder()?1:-1;
//                    return o1.getName().compareToIgnoreCase(o2.getName());
//                }
//
//            });
            return result.toArray(new FileDescriptor[0]);
        } catch (Throwable t) {
            if(t instanceof FileException)
                throw (FileException)t;
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        throw new FileException("Can't browse files. Network error or invalid username or password provided");
    }

    public InputStream getInputStream(FileDescriptor aDescriptor) throws FileException {
        try {
            FileObject fo = getFileObject(aDescriptor.getObject());
            return fo.getContent().getInputStream();
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        throw new FileException("Can't open file for reading");
    }

    public OutputStream getOutputStream(FileDescriptor aDescriptor) throws FileException {
        try {
            String url = "";
            if(aDescriptor.getId()==null){
                FileDescriptor desc =  aDescriptor.getParentFile();
                url = desc.getObject()+"/"+aDescriptor.getName();
            }else{
                url = aDescriptor.getObject();
            }
            FileObject fo = getFileObject(url);
            if(fo.exists()){
                renameFile(aDescriptor);
                fo = getFileObject(aDescriptor.getObject());
            }else{
                fo.createFile();
                aDescriptor.setObject(fo.getName().toString());
                aDescriptor.setId(RandomGUID.getGUID());
                aDescriptor.setChildrenAllowed(false);
                aDescriptor.setType("text/unknown");
                fixTypeByExtension(aDescriptor);
            }

            return fo.getContent().getOutputStream();
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        throw new FileException("Can't write to remote filesystem");
    }

    public RandomAccessFile getRandomAccessFile(FileDescriptor aDescriptor, String aAccess) throws FileException {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void createFolder(FileDescriptor aDescriptor) throws FileException {
        try {
            if(aDescriptor.getId()==null){
                FileDescriptor desc =  aDescriptor.getParentFile();
                String url = desc.getObject()+"/"+aDescriptor.getName();
                FileObject fo = getFileObject(url);
                fo.createFolder();
                aDescriptor.setType("folder");
                aDescriptor.setAllowedFileTypes(new org.webos.java.base.fs.FileType[]{FolderFileType.DEFAULT_INSTANCE, FileManager.PLAIN_FILE_TYPE});
            }else{
                renameFile(aDescriptor);
            }
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
    }

    public void renameFile(FileDescriptor aDescriptor) throws FileException {
        try {
            FileObject fo = getFileObject(aDescriptor.getObject());
            if(!fo.getName().getBaseName().equalsIgnoreCase(aDescriptor.getName())){
                String url = fo.getName().getParent()+"/"+aDescriptor.getName();
                FileObject another = getFileObject(url);
                fo.moveTo(another);
                aDescriptor.setObject(url);
            }
            return;
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        throw new FileException("Can't rename file");
    }

    public boolean removeFile(FileDescriptor aDescriptor, boolean aSafe) throws FileException {
        try {
            FileObject fo = getFileObject(aDescriptor.getObject());
            return fo.delete();
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        throw new FileException("Can't remove file");
    }

    public FileDescriptor getRootFile() {
        return null;
    }

}
